package controller

import (
	"ahutoj/web/dao"
	"ahutoj/web/io/constanct"
	"ahutoj/web/io/response"
	"ahutoj/web/logic"
	"ahutoj/web/models"
	"ahutoj/web/utils"
	"fmt"
	"io"
	"net/http"
	"os"
	"os/exec"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
)

// @Summary	上传判题文件
// @Description
// @Tags		File
// @Accept		json
// @Produce	json
// @Param		pid		path		string	true	"题目ID"
// @Param		file	formData	file	true	"文件"
// @Success	200		{object}	response.Response
// @Router		/api/file/{pid} [post]
func UpFile(ctx *gin.Context) {
	logger := utils.GetLogInstance()
	pid := ctx.Param("pid")
	//判断文件夹存在 这要求我们创建题目的时候 必须创建一个对应的文件夹
	path := utils.GetConfInstance().DataPath + "/" + pid
	logger.Debugf("path:%s", path)
	ok, _ := logic.PathExists(path)
	if !ok {
		logger.Errorf("not exists path:%s", path)
		os.Mkdir(path, os.ModeDir)
	}

	//检查文件正确
	file, err := ctx.FormFile("file")
	if err != nil {
		logger.Errorf("call FormFile filed, err=%s", err.Error())
		response.ResponseError(ctx, constanct.FILE_UP_UNSUPPORTCode)
		return
	}
	logger.Infof("upfile:%s", file.Filename)
	if !logic.CheckSuccessFile(file.Filename) {
		logger.Errorf("chekfile failed filename:%s", file.Filename)
		response.ResponseError(ctx, constanct.FILE_UP_UNSUPPORTCode)
		return
	}
	//SaveUploadedFile上传表单文件到指定的路径
	ctx.SaveUploadedFile(file, path+"/"+file.Filename)
	response.ResponseOK(ctx, response.CreateResponse(constanct.SuccessCode))
}

// @Summary	移除判题文件
// @Description
// @Tags		File
// @Accept		json
// @Produce	json
// @Param		filename	formData	string	true	"文件名"
// @Param		pid			path		string	true	"PID"
// @Success	200			{object}	response.Response
// @Router		/api/file/{pid} [delete]
func RemoveFile(ctx *gin.Context) {
	path := logic.GetPath(ctx)
	filename := ctx.PostForm("file")
	if filename == "" {
		response.ResponseError(ctx, constanct.InvalidParamCode)
		return
	}
	logger := utils.GetLogInstance()
	filepath := path + "/" + filename
	// logger.Debugf("待删除文件:%s", filepath)
	err := os.Remove(filepath)
	if err != nil {
		logger.Errorf("call Remove failed, err=%s", err.Error())
		response.ResponseError(ctx, constanct.FILE_REMOVE_FAILEDCode)
		return
	}
	response.ResponseOK(ctx, response.CreateResponse(constanct.SuccessCode))
}

// @Summary	解压文件
// @Description
// @Tags		File
// @Accept		json
// @Produce	json
// @Param		pid		path		string	true	"PID"
// @Param		file	formData	string	true	"文件名"
// @Success	200		{object}	response.Response
// @Router		/api/file/unzip/{pid} [post]
func UnzipFile(ctx *gin.Context) {
	logger := utils.GetLogInstance()
	path := logic.GetPath(ctx)
	ok, _ := logic.PathExists(path)
	if !ok {
		response.ResponseError(ctx, constanct.FILE_UNZIP_NotExistCode)
	}
	filename := ctx.PostForm("file")
	if filename == "" {
		response.ResponseError(ctx, constanct.InvalidParamCode)
	}
	filepath := path + "/" + filename
	logger.Infof("带解压文件:%s", filepath)
	cmd := exec.Command("unzip", filepath, "-d", path)
	cmd.Start()
	response.ResponseOK(ctx, response.CreateResponse(constanct.SuccessCode))
}

func CheckAndCreatDir(ctx *gin.Context, filepath string) error {
	logger := utils.GetLogInstance()
	ok, err := logic.PathExists(filepath)
	if err != nil {
		logger.Errorf("call pathExists failed,filepath:%s, err=%v", filepath, err.Error())
	}
	if !ok {
		err = os.Mkdir(filepath, 0777)
		if err != nil {
			logger.Errorf("call Mkdir failed,filepath:%s, err=%v", filepath, err.Error())
			return err
		}
	}
	return nil
}

func GetFileType(filename string) string {
	strs := strings.Split(filename, ".")
	return strs[len(strs)-1]
}

// @Summary	获取判题文件列表
// @Description
// @Tags		File
// @Accept		json
// @Produce	json
// @Param		pid	path		string	true	"题目ID"
// @Success	200	{object}	response.GetFileListResp
// @Router		/api/file/{pid} [get]
func GetFileList(ctx *gin.Context) {
	logger := utils.GetLogInstance()
	filepath := logic.GetPath(ctx)
	if filepath == "" {
		logger.Errorf("has no pid Invailed")
		response.ResponseError(ctx, constanct.InvalidParamCode)
		return
	}
	PID := ctx.Param("pid")
	ok := models.IsProblemExistByPID(ctx, &dao.Problem{PID: PID})
	if !ok {
		logger.Errorf("the problem not exist pid=%s", PID)
		response.ResponseError(ctx, constanct.InvalidParamCode)
		return
	}
	err := CheckAndCreatDir(ctx, filepath)
	if err != nil {
		logger.Errorf("call CheckAndCreatDir failed,filepath:%v,err=%v", filepath, err.Error())
		response.ResponseError(ctx, constanct.FILE_LIST_FAILEDCode)
		return
	}
	files, err := os.ReadDir(filepath)
	if err != nil {
		logger.Errorf("call ReadDir faile,filepath=%s err=%s", filepath, err.Error())
		response.ResponseError(ctx, constanct.InvalidParamCode)
		return
	}
	resp := response.GetFileListResp{}
	resp.Data = make([]response.FileItem, 0)
	for _, file := range files {
		fileinfo, _ := file.Info()
		resp.Data = append(resp.Data, response.FileItem{
			Filename: file.Name(),
			FileSize: fileinfo.Size(),
			FileType: GetFileType(file.Name()),
		})
	}
	resp.Response = response.CreateResponse(constanct.SuccessCode)
	resp.Count = len(resp.Data)
	response.ResponseOK(ctx, resp)
}

// @Summary	查看判题数据内容
// @Description
// @Tags		File
// @Accept		json
// @Produce	json
// @Param		pid			path		string	true	"题目ID"
// @Param		filename	path		string	true	"文件名"
// @Success	200			{object}	response.GetFileResp
// @Router		/api/file/{pid}/{filename} [get]
func GetFile(ctx *gin.Context) {
	logger := utils.GetLogInstance()
	filepath := logic.GetPath(ctx)
	if filepath == "" {
		logger.Errorf("has no pid Invailed")
		response.ResponseError(ctx, constanct.InvalidParamCode)
		return
	}
	filename := ctx.Param("filename")
	file := filepath + "/" + filename
	ok, err := logic.PathExists(file)
	if err != nil {
		logger.Errorf("call pathExists failed,filepath:%s, err=%v", file, err.Error())
		response.ResponseError(ctx, constanct.ServerErrorCode)
	}
	if !ok {
		logger.Errorf("call pathExists failed,filepath:%s, err=%v", file, err.Error())
		response.ResponseError(ctx, constanct.FILE_NotExistCode)
	}
	resp := &response.GetFileResp{
		Response: response.CreateResponse(constanct.SuccessCode),
		Filename: filename,
		Content:  readFileToString(file),
	}
	response.ResponseOK(ctx, resp)
}

func readFileToString(file string) string {
	logger := utils.GetLogInstance()
	File, err := os.Open(file)
	if err != nil {
		logger.Errorf("Open failed, file:%s, err=%v", file, err.Error())
		return ""
	}
	defer File.Close()
	content, err := io.ReadAll(File)
	if err != nil {
		logger.Errorf("ReadAll failed, file:%s, err=%v", file, err.Error())
		return ""
	}
	return string(content)
}

func checkImageFile(filename string) bool {
	logger := utils.GetLogInstance()
	var passImageSuffix = []string{
		"png", "jpg", "jpeg",
	}
	filenames := strings.Split(filename, ".")
	logger.Debugf("filenames:%+v", filenames)
	if len(filenames) == 1 {
		return false
	}
	suffix := filenames[len(filenames)-1]
	for _, data := range passImageSuffix {
		if data == suffix {
			return true
		}
	}
	return false
}



// @Summary	上传图片文件
// @Description
// @Tags		File
// @Accept		json
// @Produce	json
// @Param		file	formData	file	true	"文件"
// @Success	200		{object}	response.ImageResp
// @Router		/api/file/image/ [post]
func UpImagefile(ctx *gin.Context) {
	logger := utils.GetLogInstance()
	file, err := ctx.FormFile("file")
	if err != nil {
		logger.Errorf("call FormFile filed, err=%s", err.Error())
		response.ResponseError(ctx, constanct.FILE_UPIMAGE_FAILED)
		return
	}
	logger.Infof("upfile:%s", file.Filename)
	if !checkImageFile(file.Filename) {
		logger.Errorf("chekfile failed filename:%s", file.Filename)
		response.ResponseError(ctx, constanct.FILE_UPIMAGE_FAILED)
		return
	}

	imagePath := utils.GetConfInstance().ImagePath
	//SaveUploadedFile上传表单文件到指定的路径
	err = CheckAndCreatDir(ctx, imagePath)
	if err != nil {
		logger.Errorf("call CheckAndCreatDir failed imagePath:%s", imagePath)
		response.ResponseError(ctx, constanct.FILE_UPIMAGE_FAILED)
		return
	}
	name := utils.BuildMD5FileName(file)
	ctx.SaveUploadedFile(file, imagePath+name)
	response.ResponseOK(ctx, response.ImageResp{
		Response: response.CreateResponse(constanct.SuccessCode),
		ImageURL: "./image/" + name,
	})
}

// @Summary	下载题目数据到JSON
// @Description
// @Tags		File
// @Accept		json
// @Produce	json
// @Param		PIDs	query		string	true	"题目ID"
// @Success	200		{object}	response.Response
// @Router		/api/file/json/download [get]
func DownloadProblemFromJson(ctx *gin.Context) {
	logger := utils.GetLogInstance()
	req := ctx.Query("PIDs")
	if req == "" {
		logger.Errorf("call Param failed")
		response.ResponseError(ctx, constanct.InvalidParamCode)
		return
	}

	resp, err := logic.DownloadProblemFromJson(ctx, req)
	if err != nil {
		logger.Errorf("call DownloadProblemFromJson failed, req=%+v, err=%v", utils.Sdump(req), err.Error())
		response.ResponseError(ctx, constanct.ServerErrorCode)
		return
	}
	/*返回先下载文件*/
	if str, ok := resp.(string); ok {
		fileName := fmt.Sprintf("%v.json", time.Now().Unix())
		ctx.Header("Content-Type", "application/octet-stream")
		ctx.Header("Content-Disposition", "attachment; filename="+fileName) // 用来指定下载下来的文件名
		ctx.Header("Content-Transfer-Encoding", "binary")
		ctx.String(http.StatusOK, str)
	} else {
		response.ResponseOK(ctx, resp)
	}
}

// @Summary	上传题目
// @Description
// @Tags		File
// @Accept		json
// @Produce	json
// @Param		file	formData	file	true	"文件"
// @Success	200		{object}	response.Response
// @Router		/api/file/problem [post]
func UpProblemFile(ctx *gin.Context) {
	logger := utils.GetLogInstance()
	file, err := ctx.FormFile("file")
	if err != nil {
		logger.Errorf("call FormFile filed, err=%s", err.Error())
		response.ResponseError(ctx, constanct.FILE_UP_UNSUPPORTCode)
		return
	}
	resp, err := logic.UpProblemFile(ctx, file)
	if err != nil {
		logger.Errorf("call UpProblemFile failed, req=%+v, err=%v", utils.Sdump(file.Filename), err.Error())
		response.ResponseError(ctx, constanct.ServerErrorCode)
		return
	}
	response.ResponseOK(ctx, resp)
}
